﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using DataCenter.Interface;
using DataCenter.Interface.Model;
using DataCenter.Share;
using System.Diagnostics;

namespace DataCenter.Component.Manager
{
    public class CalculateManager : IManager
    {
        public CalculateManager(SqlCommand command, EntityCollection collection)
        {
            this._Command = command;
            this._TableSetting = TableSetting.GetInstance();
            this._Collection = collection;
        }
        private SqlCommand _Command;
        private TableSetting _TableSetting;
        private EntityCollection _Collection;
        private DateTime Time0;
        private DateTime Time1;

        #region IManager Members

        public bool Initialize(DateTime time0, DateTime time1)
        {
            this.Time0 = time0;
            this.Time1 = time1;

            return InitObjectValue();
        }

        public bool Execute()
        {
            foreach (ObjectEntity objectEntity in _Collection.DictObject.Values)
            {
                if (objectEntity.IsVirtual && objectEntity.CanCalculate)
                {
                    Dictionary<int, ObjectEntity> dictObject = GetComputeObjects(objectEntity);

                    ComponentEntity calculatorEntity = _Collection.GetComponent(objectEntity.BindingCalculatorId);
                    if (calculatorEntity != null)
                    {
                        using (ICalculator calculator = calculatorEntity.CreateComponent<ICalculator>(_Command))
                        {
                            try
                            {
                                if (!calculator.Initialize())
                                    continue;

                                Stopwatch watch1 = new Stopwatch();
                                watch1.Start();
                                if (!calculator.Execute(objectEntity, dictObject, Time0, Time1)) 
                                    return false;
                                watch1.Stop();
                                Logger.GetInstance().DebugFormat("\t对象{0}计算时间:{1}", objectEntity.ObjectId, watch1.ElapsedMilliseconds);

                                Stopwatch watch2 = new Stopwatch();
                                watch2.Start();
                                if (!SaveValue(objectEntity)) 
                                    return false;
                                watch2.Stop();
                                Logger.GetInstance().DebugFormat("\t对象{0}保存时间:{1}", objectEntity.ObjectId, watch2.ElapsedMilliseconds);
                            }
                            catch (Exception ex)
                            {
                                Logger.GetInstance().Error(string.Format("[ERROR]: 计算数据时发生错误。Calculator={0}, ObjectId={1}",
                                    calculatorEntity.ComponentName, objectEntity.ObjectId), ex);
                            }
                        }
                    }
                }
            }

            return true;
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            
        }

        #endregion

        private Dictionary<int, ObjectEntity> GetComputeObjects(ObjectEntity objectEntity)
        {
            Dictionary<int, ObjectEntity> dictObject = 
                new Dictionary<int, ObjectEntity>();

            int[] objectIds = objectEntity.GetComputeObjectIds();
            foreach (int objectId in objectIds)
            {
                if (!dictObject.ContainsKey(objectId))
                {
                    ObjectEntity computeObject = _Collection.GetObject(objectId);
                    if (computeObject != null)
                    {
                        dictObject.Add(computeObject.ObjectId, computeObject);
                    }
                }
            }

            return dictObject;
        }

        private bool InitObjectValue()
        {
            int sIndex = 0, eIndex = 0;
            if (_TableSetting.AdjustTimeRegion(Time0, Time1, ref sIndex, ref eIndex))
            {
                string objectIds = _Collection.GetComputeObjectIds();                
                string commandTemplet = @"SELECT ObjectId, ValueTime, ValueType, ImportTime, InterpolateTime, BinaryValue 
                                        FROM {0} WHERE ValueTime >= '" + Time0.Date + @"' AND ValueTime <= '" + Time1.Date +
                                        @"' AND ObjectId IN (" + objectIds + @")";

                StringBuilder cmdStrBuilder = new StringBuilder();
                for (int index = sIndex; index <= eIndex; index++)
                {
                    string tableName = _TableSetting.GetTableName(index);

                    cmdStrBuilder.Append(" UNION ");
                    cmdStrBuilder.AppendFormat(commandTemplet, tableName);

                    if (cmdStrBuilder.Length > 7500 || index == eIndex)
                    {
                        string commandString = cmdStrBuilder.ToString();
                        commandString = commandString.Substring(7);

                        _Command.CommandText = commandString;
                        _Command.CommandType = CommandType.Text;
                        using (SqlDataReader reader = _Command.ExecuteReader(CommandBehavior.SequentialAccess))
                        {
                            while (reader.Read())
                            {
                                BinaryRecord record = new BinaryRecord();
                                record.Build(reader);

                                ObjectEntity objectEntity = _Collection.GetObject(record.ObjectId);
                                if (objectEntity != null)
                                {
                                    if (!objectEntity.ContainRecord(record.ValueTime))
                                    {
                                        record.Build(reader, true);
                                        objectEntity.AddRecord(record);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return true;
        }

        private bool SaveValue(ObjectEntity objectEntity)
        {
            try
            {
                DateTime curTime = Time0.Date;
                while (curTime <= Time1)
                {
                    BinaryRecord record = objectEntity.GetRecord(curTime);
                    if (record != null && record.IsSubmit)
                    {
                        record.BinaryValue.BeginEdit();
                        if (record.NewRecord)
                        {
                            record.CreateRecord(_Command);
                        }
                        else
                        {
                            record.UpdateRecord(_Command, record.ImportTime0, record.ImportTime);
                        }
                        //record.IsSubmit = false;
                    }
                    curTime = curTime.AddDays(1);
                }
                objectEntity.ClearRecord();
                return true;
            }
            catch (Exception ex)
            {
                return Logger.GetInstance().Error("[ERROR]: 保存计算数据发生错误。", ex);
            }
        }
    }
}
